package com.lovely602.boot.bootdemo.utils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.TreeMap;

import org.apache.commons.lang3.StringUtils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;

public class JsonUtil {

  public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

  /**
   * 按ASCII码给json对象排序（规定：升序）
   *
   * 去除sign参数
   */
  public static String sortAsciiJson(Object object) {
    Map<String, Object> map = jsonToObject(objectToJson(object), Map.class);
    Map<String, Object> sortMap = new TreeMap<>();
    for (String key : map.keySet()) {
      if (!key.equals("sign")) {
        Object value = map.get(key);
        if (value != null) {
          sortMap.put(key, value);
        }
      }
    }
    return objectToJson(sortMap);
  }

  private static final ThreadLocal<ObjectMapper> threadLocal = ThreadLocal.withInitial(() -> {
    ObjectMapper mapper = new ObjectMapper();
    mapper.setTimeZone(TimeZone.getDefault());
    mapper.setDateFormat(new DateTimeFormat(DATE_FORMAT));
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    JavaTimeModule javaTimeModule = new JavaTimeModule();
    javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(
        DateTimeFormatter.ofPattern(DateFormatPattern.DEFAULT_LOCAL_DATE_FORMAT)));
    javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(
        DateTimeFormatter.ofPattern(DateFormatPattern.DEFAULT_DATE_FORMAT)));
    javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(
        DateTimeFormatter.ofPattern(DateFormatPattern.DEFAULT_TIME_FORMAT)));

    javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(
        DateTimeFormatter.ofPattern(DateFormatPattern.DEFAULT_LOCAL_DATE_FORMAT)));
    javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(
        DateTimeFormatter.ofPattern(DateFormatPattern.DEFAULT_DATE_FORMAT)));
    javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(
        DateTimeFormatter.ofPattern(DateFormatPattern.DEFAULT_TIME_FORMAT)));
    mapper.registerModule(javaTimeModule).registerModule(new ParameterNamesModule());
    return mapper;
  });

  public static String objectToJson(Object value) throws RuntimeException {
    if (value == null) {
      return null;
    }

    try {
      return threadLocal.get().writeValueAsString(value);
    } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
    }
  }

  public static <T> T jsonToObject(String json, Class<T> valueType) throws RuntimeException {
    if (StringUtils.isBlank(json)) {
      return null;
    }

    try {
      return threadLocal.get().readValue(json, valueType);
    } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
    }
  }

  /** 对含有泛型的返回体反序列化的支持 */
  public static <T> T jsonToObject(String json, TypeReference<T> valueType)
      throws RuntimeException {
    if (StringUtils.isBlank(json)) {
      return null;
    }

    try {
      return threadLocal.get().readValue(json, valueType);
    } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
    }
  }

  public static <T> List<T> jsonToArrayList(String json, Class<T> elementType)
      throws RuntimeException {
    if (StringUtils.isBlank(json)) {
      return new ArrayList<T>();
    }

    try {
      return threadLocal.get().readValue(json,
          threadLocal.get().getTypeFactory().constructParametricType(ArrayList.class, elementType));
    } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
    }
  }
}
